package com.zst.http.forward.remote.httppacket;

import com.zst.http.forward.remote.ForwardHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;

@Slf4j
public class HttpPacketForwardHandler implements ForwardHandler {
    private Supplier<String> remoteHostSupplier;
    private MultiplexHttpClient httpClient;

    public HttpPacketForwardHandler(Supplier<String> remoteHostSupplier) {
        if (remoteHostSupplier == null) {
            throw new IllegalArgumentException("remoteHostSupplier must not be null");
        }
        this.remoteHostSupplier = remoteHostSupplier;
        this.httpClient = new MultiplexHttpClient();
    }


    @Override
    public CompletableFuture<FullHttpResponse> doForward(FullHttpRequest request) {
        MultiplexHttpClient.Request forwardRequest = buildForwardRequest(request);

        CompletableFuture<HttpResponse> forwardFuture;
        if (request.method() == HttpMethod.GET) {
            forwardFuture = forwardRequest.doGet();
        } else if (request.method() == HttpMethod.POST) {
            forwardFuture = forwardRequest.doPost();
        } else {
            throw new IllegalArgumentException("不支持的请求方法：" + request.method());
        }


        CompletableFuture<FullHttpResponse> ret = new CompletableFuture<>();
        forwardFuture.whenComplete((httpResponse, throwable) -> {
            if (throwable != null) {
                ret.completeExceptionally(throwable);
            } else {
                try {
                    ret.complete(mapToFullHttpResponse(httpResponse));
                } catch (Exception e) {
                    ret.completeExceptionally(new RuntimeException("转换响应内容时发生错误", e));
                }
            }
        });

        return ret;
    }

    private MultiplexHttpClient.Request buildForwardRequest(FullHttpRequest request) {
        String host;
        try {
            host = remoteHostSupplier.get();
        } catch (Exception e) {
            throw new RuntimeException("获取转发目标host失败", e);
        }
        if (host == null) {
            throw new IllegalArgumentException("转发目标host为空");
        }

        String forwardUrl = buildForwardUrl(host, request.uri());
        MultiplexHttpClient.Request ret = httpClient.request().url(forwardUrl);

        request.headers().forEach((entry) -> {
            try {
                ret.setHeader(entry.getKey(), entry.getValue());
            } catch (Exception e) {
                log.debug("转发请求头时发生错误", e);
            }
        });

        HttpRequestBodyHelper.handleRequestBody(ret, request);

        return ret;
    }

    private String buildForwardUrl(String remoteHost, String uri) {
        if (remoteHost.endsWith("/")) {
            remoteHost = remoteHost.substring(0, remoteHost.length() - 1);
        }

        if (uri.startsWith("/")) {
            uri = uri.substring(1);
        }

        return remoteHost + "/" + uri;
    }

    private FullHttpResponse mapToFullHttpResponse(HttpResponse forwardResponse) throws IOException {
        FullHttpResponse ret = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
                HttpResponseStatus.valueOf(forwardResponse.getStatusLine().getStatusCode()));

        for (int i = 0; i < forwardResponse.getAllHeaders().length; i++) {
            Header h = forwardResponse.getAllHeaders()[i];
            ret.headers().add(h.getName(), h.getValue());
        }

        if (forwardResponse.getEntity() != null) {
            byte[] bodyBytes = EntityUtils.toByteArray(forwardResponse.getEntity());
            ret.content().writeBytes(bodyBytes);
        }

        return ret;
    }

}
